/*****************************************************************//**
 * \file   AnsysAPDLReader_Mat.cpp
 * \brief  AnsysAPDLReader_Mat
 *
 * \author Niuyu
 * \date   June 8, 2023
 *********************************************************************/
#pragma once
#include "AnsysAPDLReader_Select.h"
#include "CDBReader.h"
#include "StringTool.h"
#include "Macro.h"
#include <cassert>

IMPLEMENT_CDB_READER(AnsysAPDLReader_NSEL, KEY_NSEL, "NSEL")
IMPLEMENT_CDB_READER(AnsysAPDLReader_NSLE, KEY_NSLE, "NSLE")
IMPLEMENT_CDB_READER(AnsysAPDLReader_ESEL, KEY_ESEL, "ESEL")
IMPLEMENT_CDB_READER(AnsysAPDLReader_CMSEL, KEY_CMSEL, "CMSEL")
IMPLEMENT_CDB_READER(AnsysAPDLReader_ALLSEL, KEY_ALLSEL, "ALLSEL")

AnsysSelAction StringToSelectAction(std::string& str)
{
	if (str == "S")
	{
		return (AnsysSelAction)1;
	}
	else if (str == "R")
	{
		return (AnsysSelAction)2;
	}
	else if (str == "A")
	{
		return (AnsysSelAction)3;
	}
	else if (str == "U")
	{
		return (AnsysSelAction)4;
	}
	else if (str == "ALL")
	{
		return (AnsysSelAction)5;
	}
	else if (str == "NONE")
	{
		return (AnsysSelAction)6;
	}
	else if (str == "INVE")
	{
		return (AnsysSelAction)7;
	}
	else if (str == "STAT")
	{
		return (AnsysSelAction)8;
	}
	else
	{
		return (AnsysSelAction)0;
	}
}

ANSYS_TARGET_TYPE StringToComponentSelectType(std::string& str)
{
	toUpper(str);
	if (str == "ELEM")
	{
		return ANSYS_TARGET_TYPE::TARGET_ELEM;
	}
	else if (str == "NODE")
	{
		return ANSYS_TARGET_TYPE::TARGET_NODE;
	}
	else
	{
		return ANSYS_TARGET_TYPE::TARGET_UNKNOWN;
	}
}

AnsysSelItem StringToSelectItem(std::string& str)
{
	if (str == "TYPE")
	{
		return (AnsysSelItem)1;
	}
	else if (str == "REAL")
	{
		return (AnsysSelItem)2;
	}
	else
	{
		return (AnsysSelItem)0;
	}
}

bool AnsysAPDLReader_NSEL::Read(std::string& strDataLine)
{
	_nodeSelected.clear();

	_nodeSelected.selType = ANSYS_TARGET_TYPE::TARGET_NODE;
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	std::vector<double> v;
	std::string itemPara = "NODE", compPara = "",componentName = "";
	int nodeIdMin = 0, nodeIdMax = 0, nodeIdInc = 1,kabsPara = 1;
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		switch (i)
		{
		case 0:
			break;
		case 1:
			toUpper(str);
			_nodeSelected.actionType = StringToSelectAction(str);
			break;
		case 2:
			toUpper(str);
			itemPara = str;
			break;
		case 3:
			compPara = str;
			break;
		case 4:
			if (!string2Integer(str, nodeIdMin))
			{
				componentName = str;
			}
			nodeIdMax = nodeIdMin;
			break;
		case 5:
			if (!string2Integer(str, nodeIdMax))
				return false;
			break;
		case 6:
			if (!string2Integer(str, nodeIdInc))
				return false;
			break;
		case 7:
			if (!string2Integer(str, kabsPara))
				return false;
			break;
		default:
			break;
		}
	}
	if (_nodeSelected.actionType == AnsysSelAction::ALL)
	{
		_nodeSelected.selectData = _pCDBReader->GetNodeVecHlp();
	}
	else if (_nodeSelected.actionType == AnsysSelAction::S)
	{
		std::vector<int> nodeIds;
		int nodeCount = (nodeIdMax - nodeIdMin + 1) / nodeIdInc;
		nodeIds.reserve(nodeCount);
		for (size_t i = nodeIdMin; i <= nodeIdMax; i += nodeIdInc)
		{
			nodeIds.push_back(i);
		}
		_nodeSelected.selectData = nodeIds;
	}
	else if (_nodeSelected.actionType == AnsysSelAction::R)
	{
		if (!componentName.empty())
		{
			auto iter = _pCDBReader->_currentSet.find(componentName);
			if (iter != _pCDBReader->_currentSet.end())
			{
				_nodeSelected.selectData = iter->second;
			}
		}		
	}
	
	return true;
}

bool AnsysAPDLReader_NSLE::Read(std::string& strDataLine)
{
	AnsysSelect& _nodeSelected = AnsysAPDLReader_NSEL::_instance->_nodeSelected;
	_nodeSelected.clear();

	_nodeSelected.selType = ANSYS_TARGET_TYPE::TARGET_NODE;
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	std::string selectType = "S",nodeType = "ALL";
	int num = 0;
	std::vector<double> v;
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		toUpper(str);
		switch (i)
		{
		case 0:
			break;
		case 1:			
			_nodeSelected.actionType = StringToSelectAction(str);
			break;
		case 2:
			nodeType = str;
			break;
		case 3:
			if (!string2Integer(str, num))
				return false;
			break;
		default:
			break;
		}
	}

	_nodeSelected.selectData.reserve(AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.size() * 4);
	_nodeSelected.dataIndex.reserve(AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.size());

	auto& nodeMap = _pCDBReader->GetNodes();
	std::vector<AnsysElement>& _ALLelement = _pCDBReader->GetElemVec();
	std::unordered_map<int, int>& _elemMap = _pCDBReader->GetElemIdxMap();
	for (auto eleId : AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData)
	{
		auto iter = _elemMap.find(eleId);
		if (iter != _elemMap.end())
		{
			_nodeSelected.dataIndex.push_back(_nodeSelected.selectData.size());
			AnsysElement& eleM = _ALLelement[iter->second - 1];
			_nodeSelected.selectData.insert(_nodeSelected.selectData.end(), eleM.nodeIds.begin(), eleM.nodeIds.end());
		}
		else
		{
			auto iter2 = _pCDBReader->_elemAnsysIdToSetid.find(eleId);
			if (iter2 != _pCDBReader->_elemAnsysIdToSetid.end())
			{
				_nodeSelected.dataIndex.push_back(_nodeSelected.selectData.size());
				AnsysElement& eleM = _pCDBReader->_elementToSet[iter2->second - 1];
				_nodeSelected.selectData.insert(_nodeSelected.selectData.end(), eleM.nodeIds.begin(), eleM.nodeIds.end());
			}
		}
	}

	_nodeSelected.dataIndex.push_back(_nodeSelected.selectData.size());

	return true;
}

bool AnsysAPDLReader_ESEL::Read(std::string& strDataLine)
{
	_elementSelected.clear();

	std::vector<std::string> v;
	_elementSelected.selType = ANSYS_TARGET_TYPE::TARGET_ELEM;
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		toUpper(str);
		switch (i)
		{
		case 1:
			_elementSelected.actionType = StringToSelectAction(str);
			break;
		case 2:
			if (_elementSelected.actionType == AnsysSelAction::S || _elementSelected.actionType == AnsysSelAction::R
				|| _elementSelected.actionType == AnsysSelAction::A || _elementSelected.actionType == AnsysSelAction::U)
			{
				_elementSelected.item = StringToSelectItem(str);
			}
			break;
		case 3:
			if (_elementSelected.actionType == AnsysSelAction::S || _elementSelected.actionType == AnsysSelAction::R
				|| _elementSelected.actionType == AnsysSelAction::A || _elementSelected.actionType == AnsysSelAction::U)
			{
				_elementSelected.componentName = str;
			}
			break;
		case 4:
			if (_elementSelected.actionType == AnsysSelAction::S || _elementSelected.actionType == AnsysSelAction::R
				|| _elementSelected.actionType == AnsysSelAction::A || _elementSelected.actionType == AnsysSelAction::U)
			{
				v.push_back(str);
			}
			break;
		}
	}

	if (_elementSelected.item == AnsysSelItem::TYPE && _elementSelected.componentName.empty() && v.size() == 1)
	{
		int eleType = 0;
		if (!string2Integer(v[0], eleType))
		{
			_elementSelected.clear();
			return false;
		}

		AnsysPropNew _prop = _pCDBReader->GetProps();
		for (auto iter : _prop)
		{
			auto& propTuple = iter.first;
			if (std::get<1>(propTuple) == eleType)
			{
				_elementSelected.selectData = iter.second;
				break;
			}
		}
	}
	else if (_elementSelected.actionType == AnsysSelAction::ALL)
	{
		std::unordered_map<int, int>& _elemMap = _pCDBReader->GetElemIdxMap();
		std::unordered_map<int, int>& _elemOutMap = _pCDBReader->_elemAnsysIdToSetid;
		_elementSelected.selectData.resize(_elemMap.size() + _elemOutMap.size());
		int i = 0;
		for (auto iter : _elemMap)
		{
			_elementSelected.selectData[i++] = iter.first;
		}
		for (auto iter : _elemOutMap)
		{
			_elementSelected.selectData[i++] = iter.first;
		}
	}
	else if (_elementSelected.actionType == AnsysSelAction::NONE)
	{
		_elementSelected.clear();
	}
	//当选择参数是real的情况。
	else if (_elementSelected.item == AnsysSelItem::REAL && _elementSelected.componentName.empty() && v.size() == 1)
	{
		int realConstNum = 0;
		if (!string2Integer(v[0], realConstNum))
		{
			_elementSelected.clear();
			return false;
		}

		AnsysPropNew _prop = _pCDBReader->GetProps();
		for (auto iter : _prop)
		{
			auto& propTuple = iter.first;
			if (std::get<2>(propTuple) == realConstNum)
			{
				_elementSelected.selectData = iter.second;
				break;
			}
		}

	}
	return true;
}

bool AnsysAPDLReader_CMSEL::Read(std::string& strDataLine)
{
	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	std::string comName = "",entity = "";
	AnsysSelAction actionType = AnsysSelAction::S;
	std::vector<double> v;
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		switch (i)
		{
		case 0:
			break;
		case 1:
			toUpper(str);
			actionType = StringToSelectAction(str);
			break;
		case 2:
			comName = str;
			break;
		case 3:
			entity = str;
			break;
		default:
			break;
		}
	}

	AnsysComponent* _comB = nullptr;
	if (entity.empty())
	{
		//_comB = AnsysAPDLReader_CMBLOCK::_instance->getComponent(comName);
	}
	else
	{
		ANSYS_TARGET_TYPE targetType = StringToComponentSelectType(entity);
		//_comB = AnsysAPDLReader_CMBLOCK::_instance->getComponentOfType(comName, targetType);
	}

	if (_comB == nullptr&&actionType != AnsysSelAction::ALL&&actionType != AnsysSelAction::NONE)
	{
		return false;
	}
	switch (actionType)
	{
	case UNKNOWN:
		break;
	case AnsysSelAction::S:
	{
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			AnsysAPDLReader_ESEL::_instance->_elementSelected.clear();

			AnsysAPDLReader_ESEL::_instance->_elementSelected.actionType = actionType;
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selType = _comB->comType;
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData = _comB->comData;
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.clear();

			AnsysAPDLReader_NSEL::_instance->_nodeSelected.actionType = actionType;
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selType = _comB->comType;
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData = _comB->comData;
		}
		else
		{
			return false;
		}
	}
		break;
	case AnsysSelAction::R:
	{
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			AnsysAPDLReader_ESEL::_instance->_elementSelected.clear();

			AnsysAPDLReader_ESEL::_instance->_elementSelected.actionType = actionType;
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selType = _comB->comType;
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData = _comB->comData;
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.clear();

			AnsysAPDLReader_NSEL::_instance->_nodeSelected.actionType = actionType;
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selType = _comB->comType;
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData = _comB->comData;
		}
		else
		{
			return false;
		}
	}
		break;
	case AnsysSelAction::A:
	{
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.insert(AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.end(), _comB->comData.begin(), _comB->comData.end());

			std::sort(AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.begin(), AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.end());
			auto iter = std::unique(AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.begin(), AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.end());
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.erase(iter, AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.end());
			
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.insert(AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.end(), _comB->comData.begin(), _comB->comData.end());

			std::sort(AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.begin(), AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.end());
			auto iter = std::unique(AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.begin(), AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.end());
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.erase(iter, AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.end());
		}
		else
		{
			return false;
		}
	}
		break;
	case AnsysSelAction::U:
	{
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			std::vector<int>& ids = AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData;

			for (const auto& iter : _comB->comData)
			{
				auto iter2 = std::find(ids.begin(), ids.end(), iter);
				if (iter2 != ids.end())
				{
					*iter2 = -1;
				}
			}

			std::sort(ids.begin(), ids.end());
			auto iter = std::unique(ids.begin(), ids.end());
			ids.erase(iter, ids.end());

			if (ids.size() > 0 && ids[0] == -1)
			{
				ids.erase(ids.begin(), ids.begin() + 1);
			}
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			std::vector<int>& ids = AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData;

			for (const auto& iter : _comB->comData)
			{
				auto iter2 = std::find(ids.begin(), ids.end(), iter);
				if (iter2 != ids.end())
				{
					*iter2 = -1;
				}
			}

			std::sort(ids.begin(), ids.end());
			auto iter = std::unique(ids.begin(), ids.end());
			ids.erase(iter, ids.end());

			if (ids.size() > 0 && ids[0] == -1)
			{
				ids.erase(ids.begin(), ids.begin() + 1);
			}
		}
		else
		{
			return false;
		}
	}
		break;
#if 0

	case AnsysSelAction::ALL:
	{
		std::vector<AnsysComponent>& allCom = AnsysAPDLReader_CMBLOCK::_instance->getCmblock();
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			std::vector<int>& ids = AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData;
			for (const auto& iterCom : allCom)
			{
				if (iterCom.comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
				{
					ids.insert(ids.end(), iterCom.comData.begin(), iterCom.comData.end());
				}
			}

			std::sort(ids.begin(), ids.end());
			auto iter = std::unique(ids.begin(), ids.end());
			ids.erase(iter, ids.end());
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			std::vector<int>& ids = AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData;
			for (const auto& iterCom : allCom)
			{
				if (iterCom.comType == ANSYS_TARGET_TYPE::TARGET_NODE)
				{
					ids.insert(ids.end(), iterCom.comData.begin(), iterCom.comData.end());
				}
			}

			std::sort(ids.begin(), ids.end());
			auto iter = std::unique(ids.begin(), ids.end());
			ids.erase(iter, ids.end());
		}
		else
		{
			return false;
		}
	}
		break;
#endif
	case AnsysSelAction::NONE:
	{
		if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_ELEM)
		{
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.clear();
		}
		else if (_comB->comType == ANSYS_TARGET_TYPE::TARGET_NODE)
		{
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData.clear();
		}
		else
		{
			return false;
		}
	}
		break;
	default:
		break;
	}

	return true;
}

bool AnsysAPDLReader_ALLSEL::Read(std::string& strDataLine)
{
	_inBlock = ReaderBase::IN_NONE;

	std::vector<std::string> vecData;
	int n = split(strDataLine, vecData);
	std::string labelT = "ALL", entityT = "ALL";
	for (int i = 0; i < n; i++)
	{
		std::string& str = vecData[i];
		toUpper(str);
		switch (i)
		{
		case 0:
			break;
		case 1:
			labelT = str;
			break;
		case 2:
			entityT = str;
			break;
		default:
			break;
		}
	}
	EntityOrder _startEntity = stringToEntityOrder(entityT);

	if (labelT == "BELOW")
	{
		_startEntity = EntityOrder(1 + (int)_startEntity);
	}
	else if (labelT != "ALL")
	{
		return false;
	}

	for (; _startEntity != EntityOrder::UNKNOWN_ENTITY; _startEntity = EntityOrder(1 + (int)_startEntity))
	{
		switch (_startEntity)
		{
		case AnsysAPDLReader_ALLSEL::EntityOrder::ELEM_ENTITY:
		{
			std::unordered_map<int, int>& _elemMap = _pCDBReader->GetElemIdxMap();
			std::unordered_map<int, int>& _elemOutMap = _pCDBReader->_elemAnsysIdToSetid;
			AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData.resize(_elemMap.size() + _elemOutMap.size());
			int i = 0;
			for (auto iter : _elemMap)
			{
				AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData[i++] = iter.first;
			}
			for (auto iter : _elemOutMap)
			{
				AnsysAPDLReader_ESEL::_instance->_elementSelected.selectData[i++] = iter.first;
			}
		}
			break;
		case AnsysAPDLReader_ALLSEL::EntityOrder::NODE_ENTITY:
			AnsysAPDLReader_NSEL::_instance->_nodeSelected.selectData = _pCDBReader->GetNodeVecHlp();
			break;
		default:
			break;
		}
	}

	return true;
}
